Skip to main content

Yoga Custom Exercises

Detect if the user is in the correct yoga pose, such as Downward Dog or Warrior pose

This page describes a number of different techniques designed to creating custom Yoga exercises using QuickPose

Features

To show multiple features you'll need to extend your quickpose call as described in the Getting Started Guide:

// Downward Dog
let ddRightShoulder = QuickPose.Feature.rangeOfMotion(.shoulder(side: .right, clockwiseDirection: false), style: yogaStyle)
let ddRightHip = QuickPose.Feature.rangeOfMotion(.hip(side: .right, clockwiseDirection: false), style: ninetyDegreeStyle)
let ddRightKnee = QuickPose.Feature.rangeOfMotion(.knee(side: .right, clockwiseDirection: false), style: yogaStyle)

// Warrior Pose
let warriorLeftShoulder = QuickPose.Feature.rangeOfMotion(.shoulder(side: .left, clockwiseDirection: false), style: ninetyDegreeStyle)
let warriorRightShoulder = QuickPose.Feature.rangeOfMotion(.shoulder(side: .right, clockwiseDirection: true), style: ninetyDegreeStyle)
let warriorRightKnee = QuickPose.Feature.rangeOfMotion(.knee(side: .right, clockwiseDirection: false), style: yogaStyle)
let warriorLeftKnee = QuickPose.Feature.rangeOfMotion(.knee(side: .left, clockwiseDirection: false), style: ninetyDegreeStyle)

Basic Implementation​

To show results you'll need to modify your view ZStack, which we assume is setup as described in the Getting Started Guide:

ZStack(alignment: .top) {
QuickPoseCameraView(useFrontCamera: true, delegate: quickPose)
QuickPoseOverlayView(overlayImage: $overlayImage)
}

The basic implementation will require displaying some text to the screen, start with declaring this value in your SwiftUI view.

@State private var feedbackText: String? = nil

And show this feedback text as an overlay to the view in your branding.

ZStack(alignment: .top) {
QuickPoseCameraView(useFrontCamera: true, delegate: quickPose)
QuickPoseOverlayView(overlayImage: $overlayImage)
}
.overlay(alignment: .center) {
if let feedbackText = feedbackText {
Text(feedbackText)
.font(.system(size: 20, weight: .semibold)).foregroundColor(.white).multilineTextAlignment(.center)
.padding(12)
.background(RoundedRectangle(cornerRadius: 8).foregroundColor(Color("AccentColor").opacity(0.8)))
.padding(.bottom, 40)
}
}

Note the above use of alignment in .overlay(alignment: .center), you can modify this to move the overlay around easily to say the bottom: .overlay(alignment: .bottom).

Pose Detection

To detect if the user is in the correct yoga pose, check the angle ranges for the relevant joints.

For Downward Dog:

  • Right shoulder angle between 160-190 degrees
  • Right hip angle between 60-100 degrees
  • Right knee angle between 160-190 degrees

For Warrior Pose:

  • Left and right shoulder angles between 80-130 degrees
  • Right knee angle between 80-120 degrees
  • Left knee angle between 160-190 degrees
quickPose.start(features: selectedFeatures, onFrame: { status, image, features, feedback, landmarks in
switch status {
case .success:
overlayImage = image

if selectedFeatures == QuickPose.Feature.allDemoFeatures(component: "Yoga").first {
// Handle downward dog pose detection
var isDownwardDog = true

if let yogaFeatures = QuickPose.Feature.allDemoFeatures(component: "Yoga").first {
for feature in yogaFeatures {
if let result = features[feature] {
let angle = result.value

if case .rangeOfMotion(let joint, _) = feature {
switch joint {
case .shoulder(side: .right, _):
isDownwardDog = isDownwardDog && (angle >= 160 && angle <= 190)
case .hip(side: .right, _):
isDownwardDog = isDownwardDog && (angle >= 60 && angle <= 100)
case .knee(side: .right, _):
isDownwardDog = isDownwardDog && (angle >= 160 && angle <= 190)
default:
break
}
}
}
}
}

// Update feedback based on pose
if isDownwardDog {
feedbackText = "✅ Yes Downward Dog!"
} else {
feedbackText = "Not downward dog"
}

} else if selectedFeatures == QuickPose.Feature.allDemoFeatures(component: "Yoga")[1] {
// Handle warrior pose detection
var isWarrior = true

let yogaFeatures = QuickPose.Feature.allDemoFeatures(component: "Yoga")[1]
for feature in yogaFeatures {
if let result = features[feature] {
let angle = result.value

if case .rangeOfMotion(let joint, _) = feature {
switch joint {
case .shoulder(side: .left, _), .shoulder(side: .right, _):
isWarrior = isWarrior && (angle >= 80 && angle <= 130)
case .knee(side: .right, _):
isWarrior = isWarrior && (angle >= 80 && angle <= 120)
case .knee(side: .left, _):
isWarrior = isWarrior && (angle >= 160 && angle <= 190)
default:
break
}
}
}
}

// Update feedback based on pose
if isWarrior {
feedbackText = "✅ Yes Warrior Pose!"
} else {
feedbackText = "Not in warrior pose"
}
}

case .noPersonFound:
feedbackText = "Stand in view"
case .sdkValidationError:
feedbackText = "Be back soon"
}
})

Conditional Styling

To give the user visual feedback on their pose, use conditional styling to highlight joints in green when they are in the correct angle range.

let yogaStyle = QuickPose.Style(conditionalColors: [
QuickPose.Style.ConditionalColor(min: 170, max: 190, color: UIColor.green) // For straight limbs (180 degrees)
])
let ninetyDegreeStyle = QuickPose.Style(conditionalColors: [
QuickPose.Style.ConditionalColor(min: 80, max: 100, color: UIColor.green) // For 90 degree angles
])

Apply the styles when creating the yoga pose features:

// Downward Dog features
let ddRightShoulder = QuickPose.Feature.rangeOfMotion(.shoulder(side: .right, clockwiseDirection: false), style: yogaStyle)
let ddRightHip = QuickPose.Feature.rangeOfMotion(.hip(side: .right, clockwiseDirection: false), style: ninetyDegreeStyle)
let ddRightKnee = QuickPose.Feature.rangeOfMotion(.knee(side: .right, clockwiseDirection: false), style: yogaStyle)

// Warrior pose features
let warriorLeftShoulder = QuickPose.Feature.rangeOfMotion(.shoulder(side: .left, clockwiseDirection: false), style: ninetyDegreeStyle)
let warriorRightShoulder = QuickPose.Feature.rangeOfMotion(.shoulder(side: .right, clockwiseDirection: true), style: ninetyDegreeStyle)
let warriorRightKnee = QuickPose.Feature.rangeOfMotion(.knee(side: .right, clockwiseDirection: false), style: yogaStyle)
let warriorLeftKnee = QuickPose.Feature.rangeOfMotion(.knee(side: .left, clockwiseDirection: false), style: ninetyDegreeStyle)

This will highlight the user's joints in green when they are in the correct alignment for the yoga pose.